/*
 * Proto Streamer
 * Copyright (C) 2009 John Pritchard.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */
package p.s.m;

import alto.io.u.Array;
import alto.io.u.Lobjmap;
import java.util.Enumeration;
import java.util.Hashtable;
import java.lang.reflect.InvocationTargetException;

/**
 * Service manager is subclassed into a singleton.
 */
public abstract class Manager
    extends Thread
    implements p.s.Millis
{

    protected volatile long cycle = (5 * Minutes);

    protected final Lobjmap services;


    protected Manager(){
        super(p.s.Group.Top,"Service Manager");
        this.setDaemon(true);
        this.setPriority(MIN_PRIORITY);
        this.services = new Lobjmap();
    }


    public abstract Service newInstance(Object id, Class<? extends Service> group)
        throws NoSuchMethodException, InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException;

    public Service drop(Object identity){
        Lobjmap services = this.services;
        services.lockWriteEnter();
        try {
            Service service = (Service)services.remove(identity);
            if (null != service){
                try {
                    service.destroy();
                }
                catch (RuntimeException exc){
                    exc.printStackTrace();
                }
            }
            return service;
        }
        finally {
            services.lockWriteExit();
        }
    }
    public Service get(Object identity, Class<? extends Service> group){
        Lobjmap services = this.services;
        services.lockReadEnter();
        try {
            Service service = (Service)services.get(identity);
            if (null != service)
                return service;
        }
        finally {
            services.lockReadExit();
        }

        services.lockWriteEnter();
        try {
            Service service = (Service)services.get(identity);
            if (null != service)
                return service;
            else {
                try {
                    service = this.newInstance(identity,group);
                    try {
                        service.init();
                    }
                    catch (RuntimeException exc){
                        exc.printStackTrace();
                    }
                    services.put(identity,service);
                    return service;
                }
                catch (NoSuchMethodException exc){
                    throw new RuntimeException(group.getName(),exc);
                }
                catch (InstantiationException exc){
                    throw new RuntimeException(group.getName(),exc);
                }
                catch (IllegalAccessException exc){
                    throw new RuntimeException(group.getName(),exc);
                }
                catch (InvocationTargetException exc){
                    throw new RuntimeException(group.getName(),exc.getCause());
                }
            }
        }
        finally {
            services.lockWriteExit();
        }
    }
    public void run(){
        Lobjmap services = this.services;
        try {
            Array list = new Array();
            while (true){
                Thread.sleep(this.cycle);

                services.lockReadEnter();
                try {
                    Enumeration elements = services.elements();
                    while (elements.hasMoreElements()){
                        Service service = (Service)elements.nextElement();
                        if (service.hasExpired())
                            list.add(service);
                    }
                }
                finally {
                    services.lockReadExit();
                }
                if (list.isNotEmpty()){
                    services.lockWriteEnter();
                    try {
                        for (int cc = 0, count = list.size(); cc < count; cc++){
                            Service service = (Service)list.get(cc);
                            if (service.lockWriteEnterTry()){
                                try {
                                    if (service.hasExpired()){
                                        services.remove(service.getId());
                                        try {
                                            service.destroy();
                                        }
                                        catch (RuntimeException exc){
                                            exc.printStackTrace();
                                        }
                                    }
                                }
                                finally {
                                    service.lockWriteExit();
                                }
                            }
                        }
                    }
                    finally {
                        services.lockWriteExit();
                    }
                    list.clear();
                }
            }
        }
        catch (InterruptedException interrupt){
            return;
        }
    }
    public void setCycle(long millis){
        if (-1 < millis)
            this.cycle = millis;
        else
            throw new IllegalArgumentException(String.valueOf(millis));
    }
}
